local _SandBoxes = {}

class"SandBox"

function SandBox:__init()
    --Create a environment with a whitelist of functions
    self.env = {
        EnableZoomHack = EnableZoomHack,
        IsKeyPressed = IsKeyPressed,
        IsKeyDown = IsKeyDown,
        CastSpell = CastSpell,
        LevelSpell = LevelSpell,
        PrintChat = PrintChat,
        SendChat = SendChat,
        BlockChat = BlockChat,
        DrawText = DrawText,
        DrawLine = DrawLine,
        DrawCircle = DrawCircle,
        DrawArrow = DrawArrow,
        PrintFloatText = PrintFloatText,
        PingSignal = PingSignal,
        GetMyHero = GetMyHero,
        GetTarget = GetTarget,
        GetTickCount = GetTickCount,
        GetLatency = GetLatency,
        GetGameTimer = GetGameTimer,
        GetInGameTimer = GetInGameTimer,
        GetCursorPos = GetCursorPos,
        WorldToScreen = WorldToScreen,
        createSprite = createSprite,
        CLoLPacket = CLoLPacket,
        SendPacket = SendPacket,
        BuyItem = BuyItem,
        SellItem = SellItem,
        IsItemPurchasable = IsItemPurchasable,
        IsRecipePurchasable = IsRecipePurchasable,
        IsWallOfGrass = IsWallOfGrass,
        UpdateWindow = UpdateWindow,
        GetKey = GetKey,
        RGBA = RGBA,
        RGB = RGB,
        --KillProcess = KillProcess,
        GetWebRequest = GetWebRequest,
        GetAyncWebRequest = GetAsyncWebRequest,
        objManager = objManager,
        heroManager = heroManager,
        mousePos = mousePos,
        cameraPos = cameraPos,
        READY = READY,
        NOTLEARNED = NOTLEARNED,
        SUPRESSED = SUPRESSED,
        COOLDOWN = COOLDOWN,
        NOMANA = NOMANA,
        UNKNOWN = UNKNOWN,
        KEY_DOWN = KEY_DOWN,
        KEY_UP = KEY_UP,
        WM_MOUSEMOVE = WM_MOUSEMOVE,
        WM_LBUTTONDOWN = WM_LBUTTONDOWN,
        WM_LBUTTONUP = WM_LBUTTONUP,
        WM_LBUTTONUP = WM_LBUTTONUP,
        WM_RBUTTONDOWN = WM_RBUTTONDOWN,
        WM_RBUTTONUP = WM_RBUTTONUP,
        PING_NORMAL = PING_NORMAL,
        PING_FALLBACK = PING_FALLBACK_FALLBACK,
        TEAM_NONE = TEAM_NONE,
        TEAM_BLUE = TEAM_BLUE,
        TEAM_RED = TEAM_RED,
        TEAM_NEUTRAL = TEAM_NEUTRAL,
        TEAM_ENEMY = TEAM_ENEMY,
        WINDOW_X = WINDOW_X,
        WINDOW_Y = WINDOW_Y,
        WINDOW_W = WINDOW_W,
        WINDOW_H = WINDOW_H,
        _Q = _Q,
        _W = _W,
        _E = _E,
        _R = _R,
        SPELL_1 = SPELL_1,
        SPELL_2 = SPELL_2,
        SPELL_3 = SPELL_3,
        SPELL_4 = SPELL_4,
        ITEM_1 = ITEM_1,
        ITEM_2 = ITEM_2,
        ITEM_3 = ITEM_3,
        ITEM_4 = ITEM_4,
        ITEM_5 = ITEM_5,
        ITEM_6 = ITEM_6,
        RECALL = RECALL,
        SUMMONER_1 = SUMMONER_1,
        SUMMONER_2 = SUMMONER_2,
        FLOATTEXT_INVULNERABLE = FLOATTEXT_INVULNERABLE,
        FLOATTEXT_SPECIAL = FLOATTEXT_SPECIAL,
        FLOATTEXT_HEAL = FLOATTEXT_HEAL,
        FLOATTEXT_MANAHEAL = FLOATTEXT_MANAHEAL,
        FLOATTEXT_MANADMG = FLOATTEXT_MANADMG,
        FLOATTEXT_DODGE = FLOATTEXT_DODGE,
        FLOATTEXT_CRITICAL = FLOATTEXT_CRITICAL,
        FLOATTEXT_EXPERIENCE = FLOATTEXT_EXPERIENCE,
        FLOATTEXT_GOLD = FLOATTEXT_GOLD,
        FLOATTEXT_LEVEL = FLOATTEXT_GOLD,
        FLOATTEXT_DISABLE = FLOATTEXT_DISABLE,
        FLOATTEXT_QUESTRECV = FLOATTEXT_QUESTRECV,
        FLOATTEXT_QUESTDONE = FLOATTEXT_QUESTDONE,
        FLOATTEXT_SCORE = FLOATTEXT_SCORE,
        FLOATTEXT_PHYSDMG = FLOATTEXT_PHYSDMG,
        FLOATTEXT_MAGICDMG = FLOATTEXT_MAGICDMG,
        FLOATTEXT_TRUEDMG = FLOATTEXT_TRUEDMG,
        FLOATTEXT_ENEMYPHYSDMG = FLOATTEXT_ENEMYPHYSDMG,
        FLOATTEXT_ENEMYMAGICDMG = FLOATTEXT_ENEMYMAGICDMG,
        FLOATTEXT_ENEMYTRUEDMG = FLOATTEXT_ENEMYTRUEDMG,
        FLOATTEXT_ENEMYCRITICAL = FLOATTEXT_ENEMYCRITICAL,
        FLOATTEXT_COUNTDOWN = FLOATTEXT_COUNTDOWN,
        FLOATTEXT_LEGACY = FLOATTEXT_LEGACY,
        FLOATTEXT_LEGACYCRITICAL = FLOATTEXT_LEGACYCRITICAL,
        FLOATTEXT_DEBUG = FLOATTEXT_DEBUG,
        ipairs = ipairs,
        next = next,
        pairs = pairs,
        pcall = pcall,
        tonumber = tonumber,
        tostring = tostring,
        type = type,
        unpack = unpack,
        coroutine = {
            create = coroutine.create,
            resume = coroutine.resume,
            running = coroutine.running,
            status = coroutine.status,
            wrap = coroutine.wrap
        },
        string = {
            byte = string.byte,
            char = string.char,
            find = string.find,
            format = string.format,
            gmatch = string.gmatch,
            gsub = string.gsub,
            len = string.len,
            lower = string.lower,
            match = string.match,
            rep = string.rep,
            reverse = string.reverse,
            sub = string.sub,
            upper = string.upper
        },
        table = {
            insert = table.insert,
            maxn = table.maxn,
            remove = table.remove,
            sort = table.sort
        },
        math = {
            abs = math.abs,
            acos = math.acos,
            asin = math.asin,
            atan = math.atan,
            atan2 = math.atan2,
            ceil = math.ceil,
            cos = math.cos,
            cosh = math.cosh,
            deg = math.deg,
            exp = math.exp,
            floor = math.floor,
            fmod = math.fmod,
            frexp = math.frexp,
            huge = math.huge,
            ldexp = math.ldexp,
            log = math.log,
            log10 = math.log10,
            max = math.max,
            min = math.min,
            modf = math.modf,
            pi = math.pi,
            pow = math.pow,
            rad = math.rad,
            random = math.random,
            sin = math.sin,
            sinh = math.sinh,
            sqrt = math.sqrt,
            tan = math.tan,
            tanh = math.tanh
        },
        os = { clock = os.clock, difftime = os.difftime, time = os.time },
    }
    table.insert(_SandBoxes, self)
end

function SandBox:__type()
    return "SandBox"
end

function SandBox:call(fName, args)
    if self.env[fName] then
        return pcall(self.env[fName], table.unpack(args))
    end
    return nil, "No such function"
end

function SandBox:run(code)
    local f, err = load(code, nil, "t", self.env)
    if not f then return nil, err end
    return pcall(f)
end

--To Make sure all of them getting hooked:
OnLoad = OnLoad or function() end
OnDraw = OnDraw or function() end
OnTick = OnTick or function() end
OnUnload = OnUnload or function() end

--This will call the callback functions in the Sandboxes (Whitelist)
local function hook()
    local function callInSandBoxes(fName)
        for i, v in ipairs(_SandBoxes) do
            if v and v.env[fName] then
                v:call(fName,{})
            end
        end
    end

    local f = debug.getinfo(2).func
    if f == OnTick then callInSandBoxes("OnTick")
    elseif f == OnDraw then callInSandBoxes("OnDraw")
    elseif f == OnLoad then callInSandBoxes("OnLoad")
    elseif f == OnUnload then callInSandBoxes("OnUnload")
    end
end

debug.sethook(hook, "r")